﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;

#if WPF || SL
using System.Windows;
#endif

#if SL
using ExitEventHandler = System.EventHandler;
using ExitEventArgs = System.EventArgs;
#endif

namespace DemoCenter {
    public static class WebDevServerHelper {
        static Dictionary<string, Process> webServers;
#if WPF || SL
        static WebDevServerHelper() {
            Application.Current.Exit += OnApplicationExit;
        }
        static void OnApplicationExit(object sender, ExitEventArgs e) {
            CloseWebServers();
        }
#endif
        public static Dictionary<string, Process> WebServers {
            get {
                if(webServers == null)
                    webServers = new Dictionary<string, Process>();
                return webServers;
            }
        }
        public static string PrepareDemoPathForProcessing(string demoPath, string moduleName) {
            return PrepareDemoPathForProcessingCore(demoPath, GetVirtualPath(moduleName));
        }
        public static string PrepareDemoPathForProcessingCore(string demoPath, string virtualPath) {
            string physicalPath = Path.GetFullPath(demoPath).Trim();
            ushort port = GetPortNumber(physicalPath, virtualPath);
            WaitForWebServerStart(port);
            return string.Format("http://localhost:{0}/{1}/", port, virtualPath);
        }

        static void WaitForWebServerStart(ushort port) {
            do { 
                if(IsPortBusy(port))
                    return;
                System.Threading.Thread.Sleep(50);
            } while(true);
        }
        static string GetVirtualPath(string moduleName) {
            moduleName = moduleName.Replace("/", "\\");
            int startIndex = moduleName.LastIndexOf("\\") + 1;
            int length = (moduleName.Length - 4) - startIndex;
            return moduleName.Substring(startIndex, length);
        }
        static ushort GetPortNumber(string physicalPath, string virtualPath) {
            ushort portNumber;
            if(WebServers.ContainsKey(virtualPath)) {
                portNumber = GetPortNumberOutOfArguments(WebServers[virtualPath].StartInfo.Arguments);
                if(WebServers[virtualPath].HasExited)
                    WebServers[virtualPath] = RunWebDevWebServer(physicalPath, virtualPath, portNumber);
            } else {
                portNumber = GetIdlePort();
                webServers[virtualPath] = RunWebDevWebServer(physicalPath, virtualPath, portNumber);
            }
            return portNumber;
        }
        static ushort GetPortNumberOutOfArguments(string arguments) {
            int startIndex = arguments.IndexOf("port:") + 5;
            int length = arguments.IndexOf(" /path:") - startIndex;
            return ushort.Parse(arguments.Substring(startIndex, length).Trim());
        }
        static ushort GetIdlePort() {
            return GetPort(0);
        }
        static ushort GetPortNumber(ushort port) {
            if(!IsPortBusy(port))
                return port;
            return GetIdlePort();
        }
        static bool IsPortBusy(ushort port) {
            bool result = false;
            try {
                GetPort(port);
            } catch {
                result = true;
            }
            return result;
        }
        static ushort GetPort(ushort port) {
            foreach(IPAddress address in Dns.GetHostEntry("localhost").AddressList) {
                TcpListener listener = new TcpListener(address, port);
                listener.Start();
                port = (ushort)((IPEndPoint)listener.LocalEndpoint).Port;
                listener.Stop();
            }
            return port;
        }
        static Process RunWebDevWebServer(string physicalPath, string virtualPath, ushort port) {
            if(physicalPath[physicalPath.Length - 1] == Path.DirectorySeparatorChar)
                physicalPath = Path.GetDirectoryName(physicalPath);
            if(!Directory.Exists(physicalPath))
                throw new Exception(string.Format("The demo directory \"{0}\" doesn't exists", physicalPath));

            string webDevPath = GetWebDevWebServerPath();
            Process webServer = new Process();
            webServer.StartInfo.FileName = webDevPath;
            webServer.StartInfo.Arguments = string.Format("/port:{0} /path:\"{1}\" /vpath:\"/{2}/", port, physicalPath, virtualPath);
            webServer.StartInfo.UseShellExecute = true;
            webServer.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
            webServer.Start();
            return webServer;
        }
        public static void CloseWebServers() {
            foreach(KeyValuePair<string, Process> entry in WebServers) {
                if(!entry.Value.HasExited)
                    entry.Value.Kill();
            }
        }
        static string GetWebDevWebServerPath() {
            string commonProgramFiles = GetCommonProgramFilesPath();
            string vs10WebDevRelativePath = @"Microsoft Shared\DevServer\10.0\WebDev.WebServer40.exe";
            string vs8WebDevRelativePath = @"Microsoft Shared\DevServer\9.0\WebDev.WebServer.exe";
            string path = Path.Combine(commonProgramFiles, vs10WebDevRelativePath);
            if(File.Exists(path))
                return path;
            path = Path.Combine(commonProgramFiles, vs8WebDevRelativePath);
            if(File.Exists(path))
                return path;
            path = Path.Combine(Environment.GetEnvironmentVariable("WINDIR"), @"Microsoft.NET\Framework\v2.0.50727\WebDev.WebServer.exe");
            if(!File.Exists(path))
                throw new Exception("Can't find the Web Development Web Server");
            return path;
        }
        static string GetCommonProgramFilesPath() {
            string path = Environment.GetEnvironmentVariable("CommonProgramFiles(x86)");
            if(string.IsNullOrEmpty(path))
                path = Environment.GetEnvironmentVariable("CommonProgramFiles");
            if(string.IsNullOrEmpty(path))
                path = Environment.GetFolderPath(Environment.SpecialFolder.CommonProgramFiles);
            return path;
        }
    }

    public static class ModuleUrlHelper {
        public static string CorrectURLByModuleName(string moduleName) {
            string urlString = ".ur1";
            string extension = moduleName.Substring(moduleName.Length - 4, 4).ToLower();
            moduleName = moduleName.Replace("/", "\\");
            if(extension != urlString)
                return moduleName;
            return moduleName.ToLower().Replace(urlString, string.Empty);
        }
        public static bool IsUrl(string moduleName) {
            return moduleName.Substring(moduleName.Length - 4, 4).ToLower() == ".ur1";
        }
    }
}
